package cn.xinfei.xdecision.engine.runner.api;

import cn.xinfei.xdecision.common.datamodel.DecisionResult;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;
import cn.xinfei.xdecision.engine.runner.service.DecisionResultService;
import cn.xinfei.xdecision.engine.runner.service.EngineReentryService;
import cn.xinfei.xdecision.model.DecisionReentryApiRequest;
import cn.xinfei.xdecision.model.DecisionReentryResponse;
import com.dianping.cat.Cat;
import com.github.pagehelper.PageInfo;
import io.micrometer.core.instrument.Metrics;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;


@Slf4j
@Service
@RefreshScope
public class DecisionRerunApi {

    @Autowired
    private EngineReentryService engineReentryService;

    @Resource(name = "reentryThreadPool")
    private ExecutorService reentryThreadPool;

    @Autowired
    private DecisionResultService decisionResultService;

    private static final String PENDING_REENTRY = "LOCK_PENDING_REENTRY_";

    private static final ConcurrentHashMap<String, Long> PROCESSING_IDS = new ConcurrentHashMap();

    /**
     * 重入pending的redis锁
     */
    @Value("${xdecision.reentry.timeout}")
    private volatile int reentryTimeout = 60;

    public DecisionReentryResponse rerun(DecisionReentryApiRequest apiRequest) {
        long start = System.currentTimeMillis();
        DecisionReentryResponse decisionApiResponse = new DecisionReentryResponse();
        try {
//            String bizId = apiRequest.getRequestId();
//            if (StringUtils.isNotEmpty(bizId)) {
//                MDC.put(GlobalConstant.TRACE_ID, bizId);
//            }
//            String decisionId1 = apiRequest.getDecisionId();
//            if (StringUtils.isNotEmpty(decisionId1)) {
//                MDC.put(GlobalConstant.PID, decisionId1);
//            }
//            //pending中的main分支记录
//            List<DecisionResult> resultPendingList = getRerunDecisionResult(apiRequest, PROCESSING_IDS);
//
//            //pending中的最后一个异常记录
//            List<DecisionResult> lastPendingResults = findLastPendingDocument(resultPendingList);
//
//            List<EngineApiResponse> responses = new ArrayList<>();
//            int size = lastPendingResults.size();
//            decisionApiResponse.setTotal(size);
//            for (int i = 0; i < size; i++) {
//                DecisionResult decisionResult = lastPendingResults.get(i);
//                String requestId = decisionResult.getRequestId();
//                String decisionId = decisionResult.getDecisionId();
//                MDC.put(GlobalConstant.TRACE_ID, requestId);
//                MDC.put(GlobalConstant.PID, decisionId);
//                reentryThreadPool.submit(() -> {
//                    long reentryStart = System.currentTimeMillis();
//                    MDC.put(GlobalConstant.TRACE_ID, requestId);
//                    MDC.put(GlobalConstant.PID, decisionId);
//                    String engineCode = decisionResult.getEngineCode();
//                    String lockKey = PENDING_REENTRY + requestId;
//                    String ipAddress = IpUtil.getIpAddress();
//                    Boolean expire = RedisLock.tryLock(ipAddress, lockKey, reentryTimeout);
//                    if (expire) {
//                        try {
//                            Long id = decisionResult.getId();
//                            PROCESSING_IDS.put(requestId, id);
//                            //再次判断状态是否已经是终态，如果是终态则不处理
//                            DecisionResult decisionResultNew = decisionResultService.selectDecisionResultByDecisionId(requestId, decisionId);
//                            List<EngineApiResponse> engineApiResponses = engineReentryService.reentry(requestId, decisionId, engineCode, "XXL", ipAddress, lockKey);
//                            responses.addAll(engineApiResponses);
//                        } catch (Exception e) {
//                            log.error("重跑接口处理异常，decisionId={},msg={}", e.getMessage(), decisionId, e);
//                            Cat.logError(e);
//                            Metrics.counter("xengine_rerun_exception_total").increment();
//                        } finally {
//                            long reentryEnd = System.currentTimeMillis();
//                            long reentryDuration = reentryEnd - reentryStart;
//                            Metrics.gauge("xengine_reentry_respond", PrometheusTagsConstants.XENGINE_API_RESPOND_TAGS, reentryDuration);
//                            log.info("rerunDuration={}", reentryDuration);
//                            PROCESSING_IDS.remove(requestId);
//                            RedisLock.releaseLock(ipAddress, lockKey);
//                            MDC.remove(GlobalConstant.NID);
//                            MDC.remove(GlobalConstant.PID);
//                            MDC.remove(GlobalConstant.TRACE_ID);
//                            MDC.clear();
//                        }
//                    } else {
//                        //暂时info,稳定后改未debug
//                        log.info("申请分布式锁失败，clientId={},lockKey={}", ipAddress, lockKey);
//                    }
//                });
//            }
//            //只是说明任务执行成功，不代表业务含义，只说明没有异常
//            decisionApiResponse.setCode("000000");
//            decisionApiResponse.setResult(JSONArray.toJSONString(responses));
//            long respond = System.currentTimeMillis() - start;
//            log.info("rerun respond={},decisionApiResponse={}", respond, decisionApiResponse);
        } catch (Exception e) {
            log.error("重入接口异常，error={},apiRequest={}", e.getMessage(), JsonUtil.toJson(apiRequest), e);
            decisionApiResponse.setMsg("重入接口异常:" + e.getMessage());
            Cat.logError(e);
            Metrics.counter("xengine_rerun_exception_total").increment();
        }


        return decisionApiResponse;
    }

    private List<DecisionResult> findLastPendingDocument(List<DecisionResult> resultPendingList) {
        int size = resultPendingList.size();
        List<DecisionResult> lastPendingResults = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            DecisionResult decisionResult = resultPendingList.get(i);
            String requestId = decisionResult.getRequestId();
            String decisionId = decisionResult.getDecisionId();

            Map<String, DecisionResult> decisionIdMap = new HashMap<>();
            Map<String, String> parentDecisionIdMap = new HashMap<>();
            //查询当前requestId下的左右决策记录,从当前记录列表中，找出最后一个pending的记录
//            List<DecisionResult> decisionResultList = decisionResultService.getDecisionResultByBizIdAndDecisionId(requestId);
//            Optional<DecisionResult> first = decisionResultList.stream().filter(r -> r.getDecisionId().equals(decisionResult.getDecisionId())).findFirst();
//            if (first.isPresent()) {
//                DecisionResult lastPendingResult = first.get();
//                lastPendingResults.add(lastPendingResult);
//            }
        }
        return lastPendingResults;
    }

    /**
     * 查询pending中的决策请求快照
     *
     * @param apiRequest
     * @return
     */
    private List<DecisionResult> getRerunDecisionResult(DecisionReentryApiRequest apiRequest, ConcurrentHashMap<String, Long> processingIds) {
        List<String> states = new ArrayList<>();
        Date startTime = apiRequest.getStart();
        Date endTime = apiRequest.getEnd();

        //如果传入了state，则优先使用state状态
        if (StringUtils.isNotEmpty(apiRequest.getCode())) {
            states = Arrays.asList(apiRequest.getCode());
        }

        PageInfo<DecisionResult> decisionResultPending = decisionResultService.getRequestIdsByReentryRequest(apiRequest, states, startTime, endTime, PROCESSING_IDS);
        if (null == decisionResultPending) {
            return null;
        }
        return decisionResultPending.getList();
    }

}
